/*
 * Simple Open EtherCAT Master Library
 *
 * File    : nicdrv.c
 * Version : 1.3.1
 * Date    : 11-03-2015
 * Copyright (C) 2005-2015 Speciaal Machinefabriek Ketels v.o.f.
 * Copyright (C) 2005-2015 Arthur Ketels
 * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven
 * Copyright (C) 2014-2015 rt-labs AB , Sweden
 *
 * SOEM is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License version 2 as published by the Free
 * Software Foundation.
 *
 * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * As a special exception, if other files instantiate templates or use macros
 * or inline functions from this file, or you compile this file and link it
 * with other works to produce a work based on this file, this file does not
 * by itself cause the resulting work to be covered by the GNU General Public
 * License. However the source code for this file must still be made available
 * in accordance with section (3) of the GNU General Public License.
 *
 * This exception does not invalidate any other reasons why a work based on
 * this file might be covered by the GNU General Public License.
 *
 * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual
 * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for
 * the sole purpose of creating, using and/or selling or otherwise distributing
 * an EtherCAT network master provided that an EtherCAT Master License is obtained
 * from Beckhoff Automation GmbH.
 *
 * In case you did not receive a copy of the EtherCAT Master License along with
 * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany
 * (www.beckhoff.com).
 */

/** \file
 * \brief
 * EtherCAT RAW socket driver.
 *
 * Low level interface functions to send and receive EtherCAT packets.
 * EtherCAT has the property that packets are only send by the master,
 * and the send packets allways return in the receive buffer.
 * There can be multiple packets "on the wire" before they return.
 * To combine the received packets with the original send packets a buffer
 * system is installed. The identifier is put in the index item of the
 * EtherCAT header. The index is stored and compared when a frame is recieved.
 * If there is a match the packet can be combined with the transmit packet
 * and returned to the higher level function.
 *
 * The socket layer can exhibit a reversal in the packet order (rare).
 * If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
 * will reorder the packets automatically.
 *
 * The "redundant" option will configure two sockets and two NIC interfaces.
 * Slaves are connected to both interfaces, one on the IN port and one on the
 * OUT port. Packets are send via both interfaces. Any one of the connections
 * (also an interconnect) can be removed and the slaves are still serviced with
 * packets. The software layer will detect the possible failure modes and
 * compensate. If needed the packets from interface A are resend through interface B.
 * This layer is fully transparent for the higher layers.
 */

#include <stdio.h>
#include <string.h>
#include "ethercattype.h"
#include "nicdrv.h"
#include "bsp_timer.h"
#include "DM9000.h"
#include "DM9162.h"


/** Redundancy modes */
enum
{
    /** No redundancy, single NIC mode */
    ECT_RED_NONE,
    /** Double redundant NIC connecetion */
    ECT_RED_DOUBLE
};

/** Primary source MAC address used for EtherCAT.
 * This address is not the MAC address used from the NIC.
 * EtherCAT does not care about MAC addressing, but it is used here to
 * differentiate the route the packet traverses through the EtherCAT
 * segment. This is needed to fund out the packet flow in redundant
 * confihurations. */
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
/** Secondary source MAC address used for EtherCAT. */
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };

/** second MAC word is used for identification */
#define RX_PRIM priMAC[1]
/** second MAC word is used for identification */
#define RX_SEC secMAC[1]

//extern uint8_t MAC_Send(uint8_t *data, int length);
extern uint32_t ETH_CheckFrameReceived(void);
extern void lwip_pkt_handle(void);


uint8_t MAC_Send(uint8_t *data, int length)
{
	uint8_t i;
 		memcpy((u8*)(DMATxDescToSet->Buffer1Addr), (u8*)data, length);
		/* Prepare transmit descriptors to give to DMA*/
	  i=ETH_Prepare_Transmit_Descriptors(length);
	  return i;
}




static void ecx_clear_rxbufstat(int *rxbufstat)
{
   int i;
   for(i = 0; i < EC_MAXBUF; i++)
   {
      rxbufstat[i] = EC_BUF_EMPTY;
   }
}

/** Basic setup to connect NIC to socket.
 * @param[in] port        = port context struct
 * @param[in] ifname       = Name of NIC device, f.e. "eth0"
 * @param[in] secondary      = if >0 then use secondary stack instead of primary
 * @return >0 if succeeded
 */
int ecx_setupnic(ecx_portt *port,int secondary) 
{	 
	 int i;   
		
	
	 if (secondary)
   {
      /* secondary port stuct available? */
      if (port->redport)
      {
         /* when using secondary socket it is automatically a redundant setup */
  
         port->redstate                   = ECT_RED_DOUBLE;
         port->redport->stack.txbuf       = &(port->txbuf);
         port->redport->stack.txbuflength = &(port->txbuflength);
         port->redport->stack.tempbuf     = &(port->redport->tempinbuf);
         port->redport->stack.rxbuf       = &(port->redport->rxbuf);
         port->redport->stack.rxbufstat   = &(port->redport->rxbufstat);
         port->redport->stack.rxsa        = &(port->redport->rxsa);
         ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
      }
      else
      {
         /* fail */
         return 0;
      }
   }
   else
   {
      port->lastidx           = 0;
      port->redstate          = ECT_RED_NONE;
      port->stack.txbuf       = &(port->txbuf);
      port->stack.txbuflength = &(port->txbuflength);
      port->stack.tempbuf     = &(port->tempinbuf);
      port->stack.rxbuf       = &(port->rxbuf);
      port->stack.rxbufstat   = &(port->rxbufstat);
      port->stack.rxsa        = &(port->rxsa);
      ecx_clear_rxbufstat(&(port->rxbufstat[0]));
   }  

   /* setup ethernet headers in tx buffers so we don't have to repeat it */

	 for (i = 0; i < EC_MAXBUF; i++)
   {
      ec_setupheader(&(port->txbuf[i]));
      port->rxbufstat[i] = EC_BUF_EMPTY;
   }
   ec_setupheader(&(port->txbuf2));

   return 1;
}


/** Fill buffer with ethernet header structure.
 * Destination MAC is allways broadcast.
 * Ethertype is allways ETH_P_ECAT.
 * @param[out] p = buffer
 */
void ec_setupheader(void *p)
{
   ec_etherheadert *bp;
   bp = (ec_etherheadert *)p;
   bp->da0 = htons(0xffff);
   bp->da1 = htons(0xffff);
   bp->da2 = htons(0xffff);
   bp->sa0 = htons(priMAC[0]);
   bp->sa1 = htons(priMAC[1]);
   bp->sa2 = htons(priMAC[2]);
   bp->etype = htons(ETH_P_ECAT);
}
/** Get new frame identifier index and allocate corresponding rx buffer.
 * @param[in] port        = port context struct
 * @return new index.
 */
int ecx_getindex(ecx_portt *port)
{
   int idx;
   int cnt;

   idx = port->lastidx + 1;
   /* index can't be larger than buffer array */
   if (idx >= EC_MAXBUF)
   {
      idx = 0;
   }
   cnt = 0;
   /* try to find unused index */
   while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
   {
      idx++;
      cnt++;
      if (idx >= EC_MAXBUF)
      {
         idx = 0;
      }
   }
   port->rxbufstat[idx] = EC_BUF_ALLOC;
	 if (port->redstate != ECT_RED_NONE)
   {
      port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
   }
   port->lastidx = idx;
   return idx;
}

/** Set rx buffer status.
 * @param[in] port        = port context struct
 * @param[in] idx      = index in buffer array
 * @param[in] bufstat   = status to set
 */
void ecx_setbufstat(ecx_portt *port, int idx, int bufstat)
{
    port->rxbufstat[idx] = bufstat;
   if (port->redstate != ECT_RED_NONE)
   {
      port->redport->rxbufstat[idx] = bufstat;
   }
}

/** Transmit buffer over socket (non blocking).
 * @param[in] port        = port context struct
 * @param[in] idx      = index in tx buffer array
 * @param[in] stacknumber   = 0=Primary 1=Secondary stack
 * @return socket send result
 */
int ecx_outframe(ecx_portt *port, int idx, int stacknumber)
{
	 int lp, rval;
   ec_stackT *stack;

   if (!stacknumber)
   {
      stack = &(port->stack);
		  lp = (*stack->txbuflength)[idx];
		  MAC_Send((*stack->txbuf)[idx], lp);
   }
   else
   {
      stack = &(port->redport->stack);
		  lp = (*stack->txbuflength)[idx];
		  dm9k_send_packet((*stack->txbuf)[idx], lp);
   }
   rval =1;
   (*stack->rxbufstat)[idx] = EC_BUF_TX;
   return rval;
}

/** Transmit buffer over socket (non blocking).
 * @param[in] port        = port context struct
 * @param[in] idx      = index in tx buffer array
 * @return socket send result
 */
int ecx_outframe_red(ecx_portt *port, int idx)
{
   ec_comt *datagramP;
   ec_etherheadert *ehp;
   int rval;
	
	  ehp = (ec_etherheadert *)&(port->txbuf[idx]);
   /* rewrite MAC source address 1 to primary */
    ehp->sa1 = htons(priMAC[1]);
   /* transmit over primary socket*/
   rval = ecx_outframe(port, idx, 0);
   if (port->redstate != ECT_RED_NONE)
   {   
      ehp = (ec_etherheadert *)&(port->txbuf2);
      /* use dummy frame for secondary socket transmit (BRD) */
      datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
      /* write index to frame */
      datagramP->index = idx;
		  ehp = (ec_etherheadert *)&(port->txbuf[idx]);
      /* rewrite MAC source address 1 to secondary */
      ehp->sa1 = htons(secMAC[1]);
      /* transmit over secondary socket */
      //send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
      // OBS! redundant not ACTIVE for BFIN, just added to compile
//      bfin_EMAC_send(&(port->txbuf2), port->txbuflength2);
      ecx_outframe(port, idx, 1);
      port->redport->rxbufstat[idx] = EC_BUF_TX;
   }   
   
   return rval;
}

/** Non blocking read of socket. Put frame in temporary buffer.
 * @param[in] port        = port context struct
 * @param[in] stacknumber = 0=primary 1=secondary stack
 * @return >0 if frame is available and read
 */
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
{
   int lp, bytesrx;
   ec_stackT *stack;

	
	 if (!stacknumber)
   {
			 stack = &(port->stack);
			 lp = receiveLen;
			 bytesrx=lp;
			 memcpy(*stack->tempbuf, receiveBuffer, bytesrx);
   }
   else
   {
     stack = &(port->redport->stack);
		 	lp = receiveLen_DM9000;
			bytesrx=lp;
			memcpy(*stack->tempbuf, receiveBuffer_DM9000, bytesrx);
   }
//   lp = sizeof(port->tempinbuf);
//   bytesrx = bfin_EMAC_recv((*stack->tempbuf), lp);
   port->tempinbufs = bytesrx;
   return (bytesrx > 0);
}
/** Non blocking receive frame function. Uses RX buffer and index to combine
 * read frame with transmitted frame. To compensate for received frames that
 * are out-of-order all frames are stored in their respective indexed buffer.
 * If a frame was placed in the buffer previously, the function retreives it
 * from that buffer index without calling ec_recvpkt. If the requested index
 * is not already in the buffer it calls ec_recvpkt to fetch it. There are
 * three options now, 1 no frame read, so exit. 2 frame read but other
 * than requested index, store in buffer and exit. 3 frame read with matching
 * index, store in buffer, set completed flag in buffer status and exit.
 *
 * @param[in] port        = port context struct
 * @param[in] idx         = requested index of frame
 * @param[in] stacknumber  = 0=primary 1=secondary stack
 * @return Workcounter if a frame is found with corresponding index, otherwise
 * EC_NOFRAME or EC_OTHERFRAME.
 */
int ecx_inframe(ecx_portt *port, int idx, int stacknumber)
{
	 uint16  l;
	 uint16 temp;
   int     rval;
   int     idxf;
   ec_etherheadert *ehp;
   ec_comt *ecp;
   ec_stackT *stack;
   ec_bufT *rxbuf;
//   stack = &(port->stack);

		if (!stacknumber)
      {
					stack = &(port->stack);
					if(ETH_CheckFrameReceived()) 	//检测是否收到数据包
					{ 
							receiveLen=0;
							memset(receiveBuffer, 0, sizeof(receiveBuffer));
							lwip_pkt_handle();
							memcpy((uint8*)(*stack->rxbuf)[idx], (uint8*)receiveBuffer, receiveLen);
					} 
      }
     else
      { 
        stack = &(port->redport->stack);
				receiveLen_DM9000 = 0;								/* Çå³ý½ÓÊÕµÄ³¤¶È */
		    memset(receiveBuffer_DM9000, 0, sizeof(receiveBuffer_DM9000));
				if(dm9k_receive_packet())
				 {
					memcpy((uint8*)(*stack->rxbuf)[idx], (uint8*)receiveBuffer_DM9000, receiveLen_DM9000);
         }
      }	 				 
		 rval = EC_NOFRAME;		 
		 rxbuf = &(*stack->rxbuf)[idx];
		 /* check if requested index is already in buffer ? */
		 if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD)) 
		 {
				l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
				/* return WKC */
				rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
				/* mark as completed */
				(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
		 }
		 else 
		 {
				/* non blocking call to retrieve frame from socket */
				if (ecx_recvpkt(port, stacknumber)) 
				{
					 rval = EC_OTHERFRAME;
					 ehp =(ec_etherheadert*)(stack->tempbuf);
					 /* check if it is an EtherCAT frame */
					 if (ehp->etype == htons(ETH_P_ECAT)) 
					 {
							ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]); 
							l = etohs(ecp->elength) & 0x0fff;						
							idxf = ecp->index;
							/* found index equals reqested index ? */
							if (idxf == idx) 
							{
								 /* yes, put it in the buffer array (strip ethernet header) */
								 memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
								/* return WKC */
								 l=(*stack->txbuflength)[idx] - ETH_HEADERSIZE;
								 rval = ((*rxbuf)[l-2] + ((uint16)((*rxbuf)[l-1]) << 8));
								 /* mark as completed */
								 (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
								 /* store MAC source word 1 for redundant routing info */
								 (*stack->rxsa)[idx] = etohs(ehp->sa1);
							}
							else 
							{
								 /* check if index exist? */
								 if (idxf < EC_MAXBUF) 
								 {
//										rxbuf = &(*stack->rxbuf)[idxf];
//										/* put it in the buffer array (strip ethernet header) */
//										memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
//										/* mark as received */
//										(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
//										(*stack->rxsa)[idxf] = etohs(ehp->sa1);
									  (*stack->rxbufstat)[idxf] = EC_BUF_EMPTY;
								 }
								 else 
								 {
										/* strange things happend */
								 }
							}
					 }
				}    
		 }
   return rval;
}
/** Blocking receive frame function. Calls ec_waitinframe_red().
 * @param[in] port        = port context struct
 * @param[in] idx       = requested index of frame
 * @param[in] timeout   = timeout in us
 * @return Workcounter if a frame is found with corresponding index, otherwise
 * EC_NOFRAME.
 */
int ecx_waitinframe(ecx_portt *port, int idx, int timeout)
{
   int wkc;
//   osal_timert timer;
   
//   osal_timer_start (&timer, timeout);
   wkc = ecx_waitinframe_red(port, idx, timeout);
   /* if nothing received, clear buffer index status so it can be used again */
   if (wkc <= EC_NOFRAME) 
   {
      ecx_setbufstat(port, idx, EC_BUF_EMPTY);
   }
   
   return wkc;
}
/** Blocking redundant receive frame function. If redundant mode is not active then
 * it skips the secondary stack and redundancy functions. In redundant mode it waits
 * for both (primary and secondary) frames to come in. The result goes in an decision
 * tree that decides, depending on the route of the packet and its possible missing arrival,
 * how to reroute the original packet to get the data in an other try.
 *
 * @param[in] port        = port context struct
 * @param[in] idx = requested index of frame
 * @param[in] tvs = timeout
 * @return Workcounter if a frame is found with corresponding index, otherwise
 * EC_NOFRAME.
 */
int ecx_waitinframe_red(ecx_portt *port, int idx, int timeout)
{
	 int wkc  = EC_NOFRAME;
   int wkc2 = EC_NOFRAME;
   int primrx, secrx;
   uint32_t timer2;
	
   /* if not in redundant mode then always assume secondary is OK */
   if (port->redstate == ECT_RED_NONE)
   {
      wkc2 = 0;
   }
	 timer2=bsp_GetTickCount();
   do 
   {
      /* only read frame if not already in */
      if (wkc <= EC_NOFRAME)
      {
         wkc  = ecx_inframe(port, idx, 0);
      }
      /* only try secondary if in redundant mode */
      if (port->redstate != ECT_RED_NONE)
      {   
         /* only read frame if not already in */
         if (wkc2 <= EC_NOFRAME)
            wkc2 = ecx_inframe(port, idx, 1);
      }    
   /* wait for both frames to arrive or timeout */   
   }while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && ((bsp_GetTickCount()-timer2)<timeout));
//while ((bsp_GetTickCount()-timer2)<timeout);
   /* only do redundant functions when in redundant mode */
	 if (port->redstate != ECT_RED_NONE)
   {
      /* primrx if the reveived MAC source on primary socket */
      primrx = 0xffff;
      if (wkc > EC_NOFRAME) 
      {  
         primrx = port->rxsa[idx];
      }
      /* secrx if the reveived MAC source on psecondary socket */
      secrx = 0xffff;
      if (wkc2 > EC_NOFRAME) 
      {
         secrx = port->redport->rxsa[idx];
      }
      /* primary socket got secondary frame and secondary socket got primary frame */
      /* normal situation in redundant mode */
      if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
      {
         /* copy secondary buffer to primary */
         memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
         wkc = wkc2;
      }    
      /* primary socket got nothing or primary frame, and secondary socket got secondary frame */
      /* we need to resend TX packet */ 

      if ( ((primrx == 0xffff) && (secrx == RX_SEC)) ||
           ((primrx == RX_PRIM) && (secrx == RX_SEC)) )
      {
         /* If both primary and secondary have partial connection retransmit the primary received
          * frame over the secondary socket. The result from the secondary received frame is a combined
          * frame that traversed all slaves in standard order. */
         if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
         {   
            /* copy primary rx to tx buffer */
            memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
         }      
         /* resend secondary tx */
         ecx_outframe(port, idx, 1);
				 timer2=bsp_GetTickCount();
         do 
         {
            /* retrieve frame */
            wkc2 = ecx_inframe(port, idx, 1);
         } while ((wkc2 <= EC_NOFRAME) && ((bsp_GetTickCount()-timer2)<timeout));
         if (wkc2 > EC_NOFRAME)
         {   
            /* copy secondary result to primary rx buffer */
            memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
            wkc = wkc2;
         }   
      }      
   }
   /* return WKC or EC_NOFRAME */
   return wkc;
}   

/** Blocking send and recieve frame function. Used for non processdata frames.
 * A datagram is build into a frame and transmitted via this function. It waits
 * for an answer and returns the workcounter. The function retries if time is
 * left and the result is WKC=0 or no frame received.
 *
 * The function calls ec_outframe_red() and ec_waitinframe_red().
 *
 * @param[in] port        = port context struct
 * @param[in] idx      = index of frame
 * @param[in] timeout  = timeout in us
 * @return Workcounter or EC_NOFRAME
 */
int ecx_srconfirm(ecx_portt *port, int idx, int timeout)
{
   int wkc = EC_NOFRAME;
   uint32_t timer2;  
   timer2=bsp_GetTickCount();	 
   do 
   {
      /* tx frame on primary and if in redundant mode a dummy on secondary */
		 ecx_outframe_red(port, idx);
		 if (port->redstate != ECT_RED_NONE)
    		 Delay_us(200);
		 wkc = ecx_waitinframe_red(port, idx, timeout); 
   /* wait for answer with WKC>=0 or otherwise retry until timeout */   
   } while ((wkc <= EC_NOFRAME) && ((bsp_GetTickCount()-timer2)<timeout));
   /* if nothing received, clear buffer index status so it can be used again */
	 if (wkc <= EC_NOFRAME) 
			 {
				ec_setbufstat(idx, EC_BUF_EMPTY);
			 } 
   return wkc;
}


#ifdef EC_VER1


int ec_getindex(void)
{
   return ecx_getindex(&ecx_port);
}

void ec_setbufstat(int idx, int bufstat)
{
   ecx_setbufstat(&ecx_port, idx, bufstat);
}

int ec_outframe(int idx, int stacknumber)
{
   return ecx_outframe(&ecx_port, idx, stacknumber);
}

int ec_outframe_red(int idx)
{
   return ecx_outframe_red(&ecx_port, idx);
}

int ec_inframe(int idx, int stacknumber)
{
   return ecx_inframe(&ecx_port, idx, stacknumber);
}

int ec_srconfirm(int idx, int timeout)
{
   return ecx_srconfirm(&ecx_port, idx, timeout);
}

#endif
